use std::{
    collections::HashMap,
    io,
    sync::{Arc, Mutex},
};

use crossterm::cursor::SetCursorStyle;
use memchr::{memchr, memmem};
use nu_ansi_term::Style;
use rcu::RcuLock;
use reedline::{
    default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
    ColumnarMenu, Completer, CursorConfig, DefaultHinter, EditCommand, EditMode, Emacs,
    ExternalPrinter, FileBackedHistory, Highlighter, KeyCode, KeyModifiers, Keybindings, ListMenu,
    Reedline, ReedlineEvent, ReedlineMenu, Signal, Span, Validator, Vi,
};

use crate::{
    CompleterPosFrom, MonError, MonResult, MonitorBuilder, MonitorCommand, MonitorPrinterImpl,
    MonitorPrompt,
};

/// readline 返回的信号
///
/// * 成功 => line
/// * Ctrl-C
/// * Ctrl-D
pub type MonitorSignal = Signal;

pub(crate) struct PrinterImpl {
    pub(crate) p: ExternalPrinter<String>,
}

impl MonitorPrinterImpl for PrinterImpl {
    fn print(&self, msg: String) {
        self.p.print(msg).unwrap();
    }

    fn cols(&self) -> usize {
        let (width, _) = terminal_size::terminal_size().unwrap();
        width.0 as usize
    }

    fn is_main_mon(&self) -> bool {
        true
    }
}

struct CompleterHelper {
    command_hash: Arc<RcuLock<HashMap<String, Arc<dyn MonitorCommand + Sync + Send>>>>,
}

impl Completer for CompleterHelper {
    fn complete(&mut self, line: &str, pos: usize) -> Vec<reedline::Suggestion> {
        let lock = self.command_hash.read().unwrap();
        let mut v = Vec::new();
        // 当没有命令输入时, 补全所有命令
        if pos == 0 {
            let mut all = vec![];
            lock.iter().for_each(|(k, _)| {
                all.push(k);
            });
            all.sort();
            for this in all.iter() {
                let s = reedline::Suggestion {
                    value: (*this).to_string(),
                    description: None,
                    extra: None,
                    span: Span::new(0, pos),
                    append_whitespace: true,
                };
                v.push(s);
            }
            return v;
        }

        let (command, args) = parse_line(line, pos, true).unwrap();

        // 当命令行输入处于命令上时, 补全符合的 command
        if command.len() == pos {
            let mut all = vec![];
            lock.iter().for_each(|(k, _)| {
                if k.starts_with(command) {
                    all.push(k);
                }
            });
            for this in all.iter() {
                let s = reedline::Suggestion {
                    value: (*this).to_string(),
                    description: None,
                    extra: None,
                    span: Span::new(0, pos),
                    append_whitespace: true,
                };
                v.push(s);
            }
            return v;
        }

        // OK, 剩余的命令自己去补全
        let data = lock.get(command);
        if let Some(d) = data {
            let is_space = line[0..pos].ends_with(' ');
            let result = d.mon_readline_completer(&args, is_space);
            if let Some(s) = result {
                let start = if is_space {
                    pos
                } else {
                    let this_pos = pos - args.last().unwrap().len();
                    match s.from {
                        CompleterPosFrom::Start(s) => this_pos + s,
                        CompleterPosFrom::Current(cur) => pos.wrapping_add_signed(cur),
                        CompleterPosFrom::Deafult => this_pos,
                    }
                };

                s.list.iter().for_each(|this| {
                    let sug = reedline::Suggestion {
                        value: this.to_string(),
                        description: None,
                        extra: None,
                        span: Span::new(start, pos),
                        append_whitespace: s.append_whitespace,
                    };
                    v.push(sug);
                });
            }
        }
        v
    }
}

struct HighlighterHelper;

impl Highlighter for HighlighterHelper {
    fn highlight(&self, line: &str, _: usize) -> reedline::StyledText {
        let default = (Style::new(), line.into());
        let mut styled_text = reedline::StyledText::default();
        styled_text.push(default);
        styled_text
    }
}

// 匹配 ""
struct ValidatorHelper;

impl Validator for ValidatorHelper {
    fn validate(&self, line: &str) -> reedline::ValidationResult {
        let mut count = 0;
        for i in line.bytes() {
            if i == b'\"' {
                count += 1;
            }
        }
        if count % 2 == 0 {
            reedline::ValidationResult::Complete
        } else {
            reedline::ValidationResult::Incomplete
        }
    }
}

pub(crate) struct MonitorReadline {
    readline: Mutex<Reedline>,
    prompt: MonitorPrompt,
}

impl MonitorReadline {
    fn add_menu_keybindings(keybindings: &mut Keybindings, option: &MonitorBuilder) {
        keybindings.add_binding(
            KeyModifiers::CONTROL,
            KeyCode::Char('x'),
            ReedlineEvent::UntilFound(vec![
                ReedlineEvent::Menu("history_menu".to_string()),
                ReedlineEvent::MenuPageNext,
            ]),
        );

        keybindings.add_binding(
            KeyModifiers::CONTROL | KeyModifiers::ALT,
            KeyCode::Char('x'),
            ReedlineEvent::MenuPagePrevious,
        );

        let event = vec![
            ReedlineEvent::Menu("completion_menu".to_string()),
            if option.tab_next {
                ReedlineEvent::MenuNext
            } else {
                ReedlineEvent::Edit(vec![EditCommand::Complete])
            },
        ];

        keybindings.add_binding(KeyModifiers::NONE, KeyCode::Tab, ReedlineEvent::UntilFound(event));

        keybindings.add_binding(KeyModifiers::SHIFT, KeyCode::BackTab, ReedlineEvent::MenuNext);
    }

    pub(crate) fn new(
        option: MonitorBuilder,
        command_hash: Arc<RcuLock<HashMap<String, Arc<dyn MonitorCommand + Sync + Send>>>>,
        printer: ExternalPrinter<String>,
    ) -> Self {
        let completer = Box::new(CompleterHelper { command_hash });
        let edit_mode: Box<dyn EditMode> = if option.vi {
            let mut normal_keybindings = default_vi_normal_keybindings();
            let mut insert_keybindings = default_vi_insert_keybindings();

            MonitorReadline::add_menu_keybindings(&mut normal_keybindings, &option);
            MonitorReadline::add_menu_keybindings(&mut insert_keybindings, &option);

            Box::new(Vi::new(insert_keybindings, normal_keybindings))
        } else {
            let mut keybindings = default_emacs_keybindings();
            MonitorReadline::add_menu_keybindings(&mut keybindings, &option);

            Box::new(Emacs::new(keybindings))
        };
        let highlighter = Box::new(HighlighterHelper);
        let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
        let history_menu = Box::new(ListMenu::default().with_name("history_menu"));
        let validator = Box::new(ValidatorHelper);
        let cursor_config = CursorConfig {
            vi_insert: Some(SetCursorStyle::BlinkingBar),
            vi_normal: Some(SetCursorStyle::SteadyBlock),
            emacs: None,
        };

        let mut readline = Reedline::create()
            .with_ansi_colors(option.use_color)
            .with_completer(completer)
            .with_edit_mode(edit_mode)
            .with_external_printer(printer)
            .with_highlighter(highlighter)
            .with_history_exclusion_prefix(Some(" ".into()))
            .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
            .with_menu(ReedlineMenu::HistoryMenu(history_menu))
            .with_partial_completions(true)
            .with_quick_completions(option.quick_completions)
            .with_validator(validator)
            .with_cursor_config(cursor_config)
            .use_bracketed_paste(true)
            .use_kitty_keyboard_enhancement(true);

        if let Some(h) = option.history {
            let bh = Box::new(FileBackedHistory::with_file(1000, h).unwrap());
            readline = readline.with_history(bh);
        }
        if option.hinter {
            let hinter = Box::new(
                DefaultHinter::default().with_style(Style::new().fg(nu_ansi_term::Color::DarkGray)),
            );
            readline = readline.with_hinter(hinter);
        }

        Self { readline: Mutex::new(readline), prompt: option.prompt.unwrap() }
    }

    #[inline]
    pub(crate) fn readline(&self) -> io::Result<MonitorSignal> {
        let mut lock = self.readline.lock().unwrap();
        lock.read_line(&self.prompt)
    }

    #[inline]
    pub(crate) fn sync_history(&self) -> io::Result<()> {
        let mut lock = self.readline.lock().unwrap();
        lock.sync_history()
    }

    pub(crate) fn print_history(&self) -> io::Result<()> {
        let mut lock = self.readline.lock().unwrap();
        lock.print_history()
    }

    pub(crate) fn clear_scrollback(&self) -> io::Result<()> {
        let mut lock = self.readline.lock().unwrap();
        lock.clear_scrollback()
    }
}

// 如果是补全, 为了方便补全, 忽略所有在 pos 之后位置的字符串
pub(crate) fn parse_line(line: &str, pos: usize, is_completer: bool) -> Option<(&str, Vec<&str>)> {
    let new_line = if is_completer { &line[0..pos] } else { line };

    let command_index = memchr(b' ', new_line.as_bytes());
    if let Some(ci) = command_index {
        let command = &new_line[0..ci];
        let line_arg = &new_line[ci..];
        if line_arg.contains('\"') {
            let it = memmem::find_iter(line_arg.as_bytes(), "\"");
            let mut index = vec![];
            for i in it {
                index.push(i);
            }
            let mut this = [0, 0];
            let mut vv = vec![];
            let mut prev = 0;
            for (i, j) in index.iter().enumerate() {
                let m = i % 2;
                this[m] = *j;
                if m == 1 {
                    vv.push(&line_arg[this[0] + 1..this[1]]);
                    prev = this[1] + 1;
                } else {
                    let o = line_arg[prev..this[0]].split_whitespace();
                    for oo in o {
                        vv.push(oo);
                    }
                }
            }
            let o = line_arg[prev..].split_whitespace();
            for oo in o {
                vv.push(oo);
            }
            let _ = vv.extract_if(|x| *x == "\"");
            if vv.last().unwrap().is_empty() {
                vv.pop();
            }
            return Some((command, vv));
        }
    }

    let mut line_word = new_line.split_whitespace();
    let command = line_word.next()?;
    let mut v = vec![];
    for i in line_word {
        v.push(i);
    }
    Some((command, v))
}

// index[0] = '!', 不接受参数
// index[0] = '-', 可选参数
pub(crate) fn parse_arg_type(str: &str, args: &[&str]) -> MonResult<HashMap<String, String>> {
    let mut hash = HashMap::new();
    if str.is_empty() {
        if !args.is_empty() {
            return Err(MonError::InvalidArgsType);
        }
        return Ok(hash);
    }

    let mut types = Vec::new();
    let mut is_option = false;
    for word in str.split(',').collect::<Vec<&str>>() {
        let this: Vec<&str> = word.split(':').collect();
        if this.len() != 2 {
            return Err(MonError::InvalidArgsType);
        }
        let this_option = this[1].ends_with('?');
        if is_option && !this_option {
            return Err(MonError::InvalidArgsType);
        }
        is_option = this_option;

        let this_new = if this_option { &this[1][..this[1].len() - 1] } else { this[1] };
        types.push((this_option, this[0], this_new));
    }

    if args.len() > types.len() {
        return Err(MonError::InvalidArgsType);
    }

    for (index, (option, key, str)) in types.into_iter().enumerate() {
        if args.len() <= index {
            if option {
                return Ok(hash);
            } else {
                return Err(MonError::InvalidArgsType);
            }
        }
        match str {
            "*" | "str" => {},
            "isize" => {
                args[index].parse::<isize>().map_err(|_| MonError::InvalidArgsType)?;
            },
            "usize" => {
                args[index].parse::<usize>().map_err(|_| MonError::InvalidArgsType)?;
            },
            "f64" => {
                args[index].parse::<f64>().map_err(|_| MonError::InvalidArgsType)?;
            },
            "i64" => {
                args[index].parse::<i64>().map_err(|_| MonError::InvalidArgsType)?;
            },
            "u64" => {
                args[index].parse::<u64>().map_err(|_| MonError::InvalidArgsType)?;
            },
            "bool" => {
                args[index].parse::<bool>().map_err(|_| MonError::InvalidArgsType)?;
            },
            _ => {
                return Err(MonError::InvalidArgsType);
            },
        }
        hash.insert(key.to_string(), args[index].to_string());
    }

    Ok(hash)
}
